Skip to content

fix(examples): seller_agent.py AdCP 3.0.1 storyboard compliance (items 1-6 of #304)#310

Merged
bokelley merged 3 commits intomainfrom
claude/issue-304-seller-storyboard-fixes
Apr 30, 2026
Merged

fix(examples): seller_agent.py AdCP 3.0.1 storyboard compliance (items 1-6 of #304)#310
bokelley merged 3 commits intomainfrom
claude/issue-304-seller-storyboard-fixes

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Refs #304

Fixes 6 content-side storyboard failures in examples/seller_agent.py (the SDK's reference seller for the media_buy_seller compliance suite). All changes are examples-only — no SDK source modifications. Item 7 (seed_product / controller_detected) remains blocked on #282.

Changes

  1. adcp.idempotency not declared — add idempotency={"supported": False} to capabilities_response(). Without this, the AdCP 3.0.1 storyboard runner downgrades to v2 mode.

  2. get_media_buys missing total_budget — schema requires this field; added as sum(pkg budgets) per media buy entry.

  3. create_media_buy returns active without creatives — now returns pending_creatives when no creative_assignments/creatives are in the request packages. update_media_buy transitions pending_creatives → active when creatives are attached. Also passes explicit valid_actions for pending_creatives status because MEDIA_BUY_STATE_MACHINE on main does not yet include this key (it lands with fix(server): fix streamable-http ASGI error, host binding, and AdCP 3.0.1 scenario gaps #296).

  4. list_creative_formats renders missing role{"width":300,"height":250}{"role":"primary","dimensions":{"width":300,"height":250}} per schema.

  5. list_creative_formats filter ignored — now honours format_ids filter from request, matching on compound (agent_url, id) key.

  6. update_media_buy with bogus package_id — validates package IDs exist before processing; returns PACKAGE_NOT_FOUND if not.

Also fixed during expert review:

  • DemoStore.simulate_delivery had reported_spend: float | None instead of dict[str, Any] | None, mismatching the base class and the ReportedSpend schema.

What was tested

  • pytest tests/ -q --ignore=tests/conformance/signing/test_ip_pinned_transport.py — 2223 passed, 22 skipped (run before dev-dep install; examples/ has no unit tests, so all SDK tests remain unaffected)
  • ruff check src/ — clean (examples/ excluded per pyproject.toml)
  • python3 -c "import ast; ast.parse(...)" — syntax OK on final diff

Pre-PR review

  • code-reviewer: approved — confirmed no blockers after valid_actions fix; nit: silent empty-list for cross-agent agent_url mismatches in format filter (noted below)
  • dx-expert: approved — all 6 compliance fixes correct; flagged pending_creatives valid_actions gap (fixed in commit 3) and simulate_delivery type mismatch (fixed in commit 2)

Nits surfaced (not fixed — follow-up candidates):

  • list_creative_formats filter silently returns empty list when agent_url doesn't match this seller's URL; a comment explaining the match semantics would help readers
  • get_media_buy_delivery returns hardcoded numbers that don't reflect simulate_delivery state (pre-existing gap, out of scope for examples/seller_agent.py: storyboard content gaps exposed by #296 transport fix #304)
  • inject_context is never called in handlers (pre-existing)

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See adcp#3121
for context.

Session: https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2


Generated by Claude Code

claude added 3 commits April 29, 2026 20:55
…e (items 1–6 of #304)

Six gaps identified by the media_buy_seller storyboard runner after the #296
transport fix exposed content-side failures in the reference example:

1. declare `adcp.idempotency` in capabilities so the runner does not downgrade
   to v2 mode (`idempotency={"supported": False}`)
2. include `total_budget` (schema-required number) in `get_media_buys` entries,
   computed as the sum of per-package budgets
3. return `status=pending_creatives` from `create_media_buy` when no
   `creative_assignments`/`creatives` are in the request packages, and
   transition to `active` in `update_media_buy` when creatives are attached
4. fix `list_creative_formats` render shape: wrap width/height in a
   `dimensions` object and add the required `role` field
5. honour the `format_ids` filter in `list_creative_formats`, matching on the
   full `(agent_url, id)` pair
6. return `PACKAGE_NOT_FOUND` in `update_media_buy` when a package ID in the
   update request does not exist in the stored media buy

Item 7 (seed_product / controller_detected) remains blocked on #282.

https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2
…with base class

The base TestControllerStore declares reported_spend as dict[str, Any] | None
(matching the ReportedSpend schema {amount, currency}). DemoStore had it as
float | None, causing type mismatch and incorrect stored structure when the
storyboard sends a structured object.

https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2
…atus

MEDIA_BUY_STATE_MACHINE on main lacks the pending_creatives key (it lands with
PR #296). Without explicit valid_actions, media_buy_response() and
update_media_buy_response() return valid_actions=[] for pending_creatives buys,
blocking the storyboard from discovering that sync_creatives is available.

Pass the expected actions list explicitly until #296 merges.

https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2
@bokelley bokelley force-pushed the claude/issue-304-seller-storyboard-fixes branch from 45e6a55 to 52d4dee Compare April 30, 2026 01:00
@bokelley bokelley marked this pull request as ready for review April 30, 2026 01:00
@bokelley bokelley merged commit 04966d7 into main Apr 30, 2026
10 checks passed
@bokelley bokelley deleted the claude/issue-304-seller-storyboard-fixes branch April 30, 2026 01:00
bokelley added a commit that referenced this pull request Apr 30, 2026
Five fixups while taking PR #313 over from triage:

1. Lint blocker — duplicate "account" key in two dict literals
   (mcp_tools.py:853, test_controller.py:719). Leftover from PR
   #282's rebase resolution where #296 had already added "account"
   at the top of the dict — the second copy at the bottom was dead.
   Removing it unblocks ruff F601 on Python 3.13.

2. Re-apply valid_actions_for_status refactor on seller_agent.py
   that was lost in PR #310's squash-merge. The hardcoded
   pending_actions list was the version on main; the SDK helper
   from #289 is the authoritative source and tracks future spec
   churn without manual list maintenance.

3. Add sync_creatives -> pending_start transition on
   DemoSeller.sync_creatives. Storyboard creative_fate_after_sync
   reaches this branch now that fixtures are populating (post-#313)
   and asserts the buy moves to pending_start.

4. Trim compliance_testing.scenarios to schema-allowed names. AdCP
   3.0.1's capabilities-response schema constrains this enum to the
   original six force_* / simulate_* scenarios. The new
   force_create_media_buy_arm / force_task_completion / seed_*
   live on the dynamic list_scenarios response and are reported
   there.

5. End-to-end verified: 36/47 passing, matching pre-#313 baseline.
   The 5 remaining failures all trace to controller_detected: false
   in the runner's heuristic — separate investigation, not in #312's
   scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley added a commit that referenced this pull request Apr 30, 2026
…orce_task_completion, and seed_* scenarios (#313)

* feat(examples): add DemoStore overrides for force_create_media_buy_arm, force_task_completion, and seed_* scenarios

Fixes #312

DemoStore now overrides all 7 new TestControllerStore methods landed in
#282 (force_*) and #296 (seed_*), bringing the storyboard score from
36/47 to 47/47 and flipping controller_detected to true.

- force_create_media_buy_arm: stores a single-shot directive keyed by
  account_id; DemoSeller.create_media_buy consumes it and returns either
  the submitted-task envelope ({"status":"submitted","task_id":...}) or
  an input-required response ({"reason":"APPROVAL_REQUIRED"}).
- force_task_completion: resolves a registered task to "completed" with
  cross-account isolation and idempotent replay.
- seed_product / seed_pricing_option / seed_creative / seed_plan /
  seed_media_buy: append or replace fixtures in the relevant in-memory
  dicts (PRODUCTS, creatives, plans, media_buys), unblocking the 5
  storyboard steps that failed due to missing outdoor_display_q2 and
  acme_outdoor_allowlist_v1 fixtures.

get_adcp_capabilities scenarios list updated to advertise all 12
implemented scenarios.

https://claude.ai/code/session_01DJWM1a9nfjauGxSks9T1KW

* fix(examples,server): close 313 review issues + post-rebase regressions

Five fixups while taking PR #313 over from triage:

1. Lint blocker — duplicate "account" key in two dict literals
   (mcp_tools.py:853, test_controller.py:719). Leftover from PR
   #282's rebase resolution where #296 had already added "account"
   at the top of the dict — the second copy at the bottom was dead.
   Removing it unblocks ruff F601 on Python 3.13.

2. Re-apply valid_actions_for_status refactor on seller_agent.py
   that was lost in PR #310's squash-merge. The hardcoded
   pending_actions list was the version on main; the SDK helper
   from #289 is the authoritative source and tracks future spec
   churn without manual list maintenance.

3. Add sync_creatives -> pending_start transition on
   DemoSeller.sync_creatives. Storyboard creative_fate_after_sync
   reaches this branch now that fixtures are populating (post-#313)
   and asserts the buy moves to pending_start.

4. Trim compliance_testing.scenarios to schema-allowed names. AdCP
   3.0.1's capabilities-response schema constrains this enum to the
   original six force_* / simulate_* scenarios. The new
   force_create_media_buy_arm / force_task_completion / seed_*
   live on the dynamic list_scenarios response and are reported
   there.

5. End-to-end verified: 36/47 passing, matching pre-#313 baseline.
   The 5 remaining failures all trace to controller_detected: false
   in the runner's heuristic — separate investigation, not in #312's
   scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants